import json
import time
import uuid
from datetime import datetime

from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from rest_framework.response import Response
from rest_framework.views import APIView
from car.models import Car
from shop import serializers
from shop.models import Coupon, Order, OrderDetail

from tools.myredis import r
from tools.pay import AliPaySDK
from user.models import User


def getcart(userid):
    # 获取用户购物id为 userid 的用户购物车中所有商品的键
    keys = r.get_key("users" + str(userid) + "good*")
    clist = []
    tmoney = 0
    # 遍历每个商品键，获取商品详情
    for i in keys:
        key = i.decode()
        # 获取商品的属性，存储在一个字典中
        obj = r.hash_getall(key)
        print(obj)
        dict = {}
        for k, v in obj.items():
            kk = k.decode()
            vv = v.decode()
            dict[kk] = vv
        # 从 Redis 缓存中获取商品详细信息，并更新到商品信息字典中
        try:
            # 尝试将存储在 Redis 中的商品信息解析为 Python 对象
            goods = json.loads(r.get_str('detail' + str(dict['id'])))
            dict['name'] = goods['name']
            dict['price'] = int(goods['price'])
            dict['desc'] = goods['desc']
            dict['img'] = goods['img']
            dict['num'] = int(goods['num'])
        except Car.DoesNotExist:
            # 若商品不存在，继续下一个商品的处理，可根据需要修改异常处理逻辑
            continue
        # 计算购物车中商品的总价
        tmoney += dict['price'] * int(dict['count'])
        # 将商品信息添加到已选商品列表中
        clist.append(dict)
    # 返回已选商品列表和总价
    return clist, tmoney


def getcheck(userid, goods):
    clist = []
    tmoney = 0
    # 遍历商品列表
    for i in goods:
        key = "users" + str(userid) + "good" + str(i)
        print(key)
        # 获取商品的哈希信息
        obj = r.hash_getall(key)
        goods_dict = {}
        print("obj")
        print(obj)
        for k, v in obj.items():
            # 假设键和值是字节类型，将其解码为字符串
            kk = k.decode()
            vv = v.decode()
            goods_dict[kk] = vv
            print(123456)
            print(goods_dict)
            try:
                # 从 Redis 中获取商品详细信息并更新到商品信息字典中
                goods = json.loads(r.get_str('detail' + str(i)))
                goods_dict['name'] = goods['name']
                goods_dict['price'] = int(goods['price'])
                goods_dict['desc'] = goods['desc']
                goods_dict['img'] = goods['img']
                goods_dict['num'] = int(goods['num'])
            except ObjectDoesNotExist:
                # 若商品不存在，继续下一个商品的处理，根据实际情况修改异常处理逻辑
                continue
            # 计算购物车中商品的总价
            tmoney += int(goods_dict['price']) * int(goods_dict['num'])
        # 将商品信息添加到已选商品列表中
        clist.append(goods_dict)
    # 返回已选商品列表和总价
    return clist, tmoney


def getuuid():
    # 生成一个唯一的 UUID，并存储在 Redis 中
    uid = uuid.uuid1().hex
    r.set_str('ordertoken', uid)
    return uid


class CartsView(APIView):
    def post(self, request):
        # 从请求数据中获取用户 id、商品 id 和商品数量
        userid = request.data['userid']
        goodid = request.data['goodid']
        count = request.data['count']
        # 构建商品在 Redis 中的键
        key = "users" + str(userid) + "good" + str(goodid)
        # 查询购物车中此用户是否已经添加过该商品
        goods = r.hash_getall(key)
        if goods:
            # 如果已经添加过，更新商品数量
            c = r.hash_getone(key, 'count')
            number = int(c.decode()) + count
            r.hash_add(key, 'count', number)
        else:
            # 如果未添加过，添加商品信息到购物车
            r.hash_add(key, 'id', goodid)
            r.hash_add(key, 'count', count)
            # 从 Car 模型中获取商品详细信息并存储到 Redis 中
            try:
                car = Car.objects.filter(id=goodid).first()
                print(car.price)
                good_detail = {
                    "name": car.name,
                    "price": car.price,  # 假设价格存储为浮点数，根据实际情况调整
                    "desc": car.desc,
                    "img": car.img,
                    "num": car.num
                }
                print(good_detail)
                # 将商品详细信息存储到 Redis 中
                r.set_str('detail' + str(goodid), json.dumps(good_detail))
            except Car.DoesNotExist:
                # 处理商品不存在的情况，可根据需要修改异常处理逻辑
                pass
        return Response({"code": 200})

    def get(self, request):
        # 获取用户 id
        userid = request.GET.get('userid')
        print(userid)
        # 获取用户购物车信息和总价
        clist, tmoney = getcart(userid)
        # 生成订单 token
        ordertoken = getuuid()
        print(ordertoken)
        # 返回购物车信息、总价和订单 token
        return Response({"code": 200, 'clist': clist, 'tmoney': tmoney, 'ordertoken': ordertoken})


class CountView(APIView):
    def post(self, request):
        # 从请求数据中获取用户 id、商品 id 和商品数量
        userid = request.data['userid']
        goodid = request.data['goodid']
        count = request.data['count']
        # 构建商品在 Redis 中的键
        key = "users" + str(userid) + "good" + str(goodid)
        # 查询购物车中此用户是否已经添加过该商品
        goods = r.hash_getall(key)


class CouponView(APIView):
    def get(self, request):
        # 从请求的 GET 参数中获取用户 id
        userid = request.GET.get('userid')
        goods = request.GET.get('goods')
        if not userid:
            # 若用户 id 缺失，返回错误响应
            return Response({"code": 400, "message": "userid 参数缺失"})
        # 查询购物车中已经选中的商品信息
        clist, tmoney = getcheck(userid, goods)
        if clist is None or tmoney is None:
            # 若购物车信息获取失败，返回错误响应
            return Response({"code": 500, "message": "购物车信息获取失败"})
        # 获取当前时间
        now = datetime.now()
        # 查询用户可用的优惠券，使用正确的查询条件和排序
        available_coupons = Coupon.objects.filter(
            user__id=userid, status=0, start_time__lte=now, end_time__gte=now, max_price__lte=tmoney).order_by('-price')
        ser = serializers.CouponSerializer(available_coupons, many=True)
        coupon_list = []
        for coupon in ser.data:
            # 构建优惠券的详细信息字典
            dict = {
                "id": coupon['id'],
                "name": coupon['name'],
                "condition": coupon['rule'],
                "startAt": coupon['start_time'],
                "endAt": coupon['end_time'],
                "value": int(coupon['price']) * 100,
            }
            coupon_list.append(dict)
        # 返回优惠券列表
        return Response({"code": 200, "coupon": coupon_list})


class OrdersView(APIView):
    def post(self, request):
        # 接口幂等性判断，保证只提交一次，使用 setnx 实现
        # 正确从请求数据中获取用户 id
        userid = request.data.get('userid')
        goods = request.data.get('goods')
        print(userid)
        print(goods)
        if userid:
            # if r.setnx_str('usersetnx' + str(userid), '1'):
                # 开启事务处理
                try:
                    with transaction.atomic():
                        # 获取请求中的地址 id、优惠券 id、支付类型和订单 token
                        addressid = request.data.get('addressid')
                        couponid = request.data.get('couponid')
                        paytype = request.data.get('paytype')
                        ordertoken = request.data.get('ordertoken')
                        print(addressid)
                        print(couponid)
                        print(paytype)
                        print(ordertoken)
                        if ordertoken != r.get_str('ordertoken'):
                            # 若订单号错误，返回错误响应
                            return Response({"code": 400, "message": "订单号错误"})
                        # 查询购物车信息
                        clist, tmoney = getcheck(userid, goods)
                        print("购物车信息获取成功")
                        if clist is None or tmoney is None:
                            # 若购物车信息获取失败，返回错误响应
                            return Response({"code": 500, "message": "购物车信息获取失败"})
                        # 使用雪花算法生成订单号
                        order_snowflake = Snowflake()
                        order_number = order_snowflake.generate_id()
                        # 创建订单对象，写入订单表和订单详情表
                        # 订单表包含订单号、用户、生成日期、总金额、优惠金额、实际支付、地址、优惠券、支付类型、订单状态等信息
                        user = User.objects.get(id=userid)
                        if couponid == "":
                            coupon = None
                        else:
                            coupon = Coupon.objects.get(id=couponid)
                        order = Order(
                            order_number=order_number,
                            user=user,
                            creation_date=datetime.now(),
                            total_amount=tmoney,
                            discount_amount=0,  # 初始优惠金额为 0，后续根据优惠券更新
                            actual_payment=tmoney,  # 初始实际支付为总金额，后续根据优惠券更新
                            address=addressid,
                            coupon=coupon,
                            paytype=paytype,
                            order_status='待处理'  # 初始订单状态为待处理
                        )
                        order.save()
                        # 遍历购物车中的商品列表
                        print(clist)
                        for item in clist:
                            goodsid = item.get('id')
                            price = item.get('price')
                            quantity = item.get('count')
                            image = item.get('img')
                            print(goodsid)
                            # 从商品表中获取商品信息
                            car = Car.objects.get(id=goodsid)
                            # 创建订单详情对象，包含用户、订单、商品、价格、数量、图片等信息
                            order_detail = OrderDetail(
                                user=user,
                                order=order,
                                goods=car,
                                price=price,
                                quantity=quantity,
                                image=image
                            )
                            order_detail.save()
                            # 锁定库存，更新商品表中的库存
                            print(car.id)
                            print(car.num)
                            print("库存")
                            if car.num >= int(quantity):
                                car.num -= int(quantity)
                                car.save()
                            else:
                                # 若库存不足，抛出异常
                                raise Exception("库存不足")
                        # 更新优惠券状态为已使用
                        if couponid:
                            coupon = Coupon.objects.get(id=couponid)
                            coupon.status = 1  # 假设 1 表示已使用状态
                            coupon.save()
                        # 删除用户的订单提交标记和订单 token
                        r.str_del('usersetnx' + str(userid))
                        r.str_del('ordertoken')
                        for i in goods:
                            r.str_del("users" + str(userid) + "good" + str(i))
                            r.str_del("detail" + str(i))
                        # 返回订单号
                        return Response({"code": 200, "order_number": order_number, "price": tmoney})
                except Exception as e:
                    # 处理异常，返回错误响应
                    return Response({"code": 500, "message": str(e)})
            # else:
                # 若无权操作，返回错误响应
                # return Response({"code": 403, "message": "无权操作"})
        else:
            # 若用户 id 缺失，返回错误响应
            return Response({"code": 400, "message": "userid 参数缺失"})


class Snowflake:
    def __init__(self):
        # 雪花算法的初始化参数
        self.worker_id = 1
        self.datacenter_id = 1
        self.sequence = 0
        self.twepoch = 1288834974657
        self.worker_id_bits = 5
        self.datacenter_id_bits = 5
        self.max_worker_id = -1 ^ (-1 << self.worker_id_bits)
        self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits)
        self.sequence_bits = 12
        self.worker_id_shift = self.sequence_bits
        self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits
        self.timestamp_shift = self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits
        self.sequence_mask = -1 ^ (-1 << self.sequence_bits)
        self.last_timestamp = -1

    def generate_id(self):
        # 获取当前时间戳（毫秒）
        timestamp = int(time.time() * 1000)
        if timestamp < self.last_timestamp:
            # 若时钟倒退，拒绝生成 ID
            raise Exception("时钟倒退，拒绝生成 ID")
        if timestamp == self.last_timestamp:
            # 处理同一毫秒内的情况
            self.sequence = (self.sequence + 1) & self.sequence_mask
            if self.sequence == 0:
                timestamp = self.wait_next_millis(self.last_timestamp)
        else:
            self.sequence = 0
        self.last_timestamp = timestamp
        # 生成雪花算法的唯一 ID
        id = ((timestamp - self.twepoch) << self.timestamp_shift) | (self.datacenter_id << self.datacenter_id_shift) | (
                self.worker_id << self.worker_id_shift) | self.sequence
        return id

    def wait_next_millis(self, last_timestamp):
        # 等待到下一个毫秒
        timestamp = int(time.time() * 1000)
        while timestamp <= last_timestamp:
            timestamp = int(time.time() * 1000)
        return timestamp


class OrderPayView(APIView):
    def post(self, request):
        order_number = request.data.get('order_number')
        price = request.data.get('price')
        alipay = AliPaySDK()
        orderlist = Order.objects.filter(order_number=order_number, order_status="待处理").first()
        if not orderlist:
            return Response({"code": 400, "message": "订单不存在或已支付"})
        link = alipay.page_pay(order_number, 40, "订单支付")
        orderlist.order_status = "未支付"
        orderlist.save()
        return Response({"code": 200, "message": "支付成功", "link": link})


class NotifyView(APIView):
    def get(self, request):
        data = request.query_params.dict()
        alipay = AliPaySDK()
        success = alipay.check_sign(data)
        if success:
            order_number = data.get('out_trade_no')
            orderlist = Order.objects.filter(order_number=order_number, order_status="未支付").first()
            if orderlist:
                orderlist.order_status = "已支付"
                orderlist.save()
                return Response({"code": 200, "message": "支付成功", "data": data})
            else:
                return Response({"code": 400, "message": "订单不存在或已支付"})
        else:
            return Response({"code": 400, "message": "支付失败"})
